package utils;

import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Utils {

    private final static Logger logger = LoggerFactory.getLogger(Utils.class);

    public static final String ZH_EN_NUM_SPACE_REX  = "[^\\u4e00-\\u9fa5\\d \\w]";

    public static final Pattern  ZH_EN_NUM_SPACE_PATTERN = Pattern.compile("[^\\u4e00-\\u9fa5\\d \\w]");

    public static final Pattern  EN_PATTERN = Pattern.compile("^[a-z]+");
    public static final Pattern  DIGIT_PATTERN = Pattern.compile("^[0-9]+");

    private static final String PUNCS = "[()（）|?!,.？！。，、·-]";

    public static final Pattern NUM_PATTERN = Pattern.compile("-?(\\d+\\.)?\\d+");

    public static int editDistance(String s1, String s2) {
        // 创建编辑距离矩阵
        int[][] dist = new int[s1.length()+1][s2.length()+1];

        // 初始化矩阵
        for (int i = 0; i <= s1.length(); i++) {
            dist[i][0] = i;
        }
        for (int j = 0; j <= s2.length(); j++) {
            dist[0][j] = j;
        }

        // 填充矩阵
        for (int i = 1; i <= s1.length(); i++) {
            for (int j = 1; j <= s2.length(); j++) {
                if (s1.charAt(i-1) == s2.charAt(j-1)) {
                    dist[i][j] = dist[i-1][j-1];
                } else {
                    int add = dist[i][j-1] + 1;
                    int delete = dist[i-1][j] + 1;
                    int replace = dist[i-1][j-1] + 1;
                    dist[i][j] = Math.min(add, Math.min(delete, replace));
                }
            }
        }

        // 返回编辑距离
        return dist[s1.length()][s2.length()];
    }

    public static int lcSequence(String s1, String s2) {
        int m = s1.length();
        int n = s2.length();
        int[][] dp = new int[m + 1][n + 1];

        for (int i = 0; i <= m; i++) {
            for (int j = 0; j <= n; j++) {
                if (i == 0 || j == 0) {
                    dp[i][j] = 0;
                } else if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[m][n];
    }


    public static int lcSubstring(String s1, String s2) {
        int m = s1.length();
        int n = s2.length();
        int result = 0;
        int[][] dp = new int[m + 1][n + 1];

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    result = Math.max(result, dp[i][j]);
                } else {
                    dp[i][j] = 0;
                }
            }
        }
        return result;
    }

    public static List<String> splitWord(String ipt) {
        List<String> lst = new ArrayList<>();
        if(CollectionUtils.isEmpty(lst)){
            return lst;
        }
        String s = ipt.toLowerCase();
        while (s.length() > 0) {
            Matcher enMatch = EN_PATTERN.matcher(s);
            Matcher numMatch = DIGIT_PATTERN.matcher(s);
            String word;
            if (enMatch.find()) {
                word = enMatch.group(0);
            } else if (numMatch.find()) {
                word = numMatch.group(0);
            } else {
                word = s.substring(0, 1);
            }
            lst.add(word);
            s = s.replaceFirst(word, "").trim();
        }
        return lst;
    }

    public static double bm25Score(String[] qTokens, String[] docTokens, Map<String, Double> idfDict, Double k1, Double k2, Double b) {
        if (qTokens == null || qTokens.length == 0 || docTokens == null || docTokens.length == 0
                || MapUtils.isEmpty(idfDict) || idfDict.get("AVG_DL") == null) {
            return 0.0;
        }
        if(k1 == null){
            k1 = 2.0d;
        }
        if(k2 == null){
            k2 = 1.0d;
        }
        if(b == null){
            b = 0.5d;
        }
        double score = 0.0;
        int docLen = docTokens.length;
        double avgDl = idfDict.get("AVG_DL");
        double K = k1 * (1 - b + b * docLen / avgDl);
        Map<String, Integer> qTf = new HashMap<>();
        for (String qTok : qTokens) {
            qTf.put(qTok, qTf.getOrDefault(qTok, 0) + 1);
        }
        Map<String, Integer> docTf = new HashMap<>();
        for (String docTok : docTokens) {
            docTf.put(docTok, docTf.getOrDefault(docTok, 0) + 1);
        }
        for (String tok : qTokens) {
            if (!idfDict.containsKey(tok)) {
                continue;
            }
            double wi = idfDict.get(tok);
            int fi = docTf.containsKey(tok) ? docTf.get(tok) : 0;
            double ri = fi * (k1 + 1) / (fi + K) * qTf.get(tok) * (k2 + 1) / (qTf.get(tok) + k2);
            score += (wi * ri);
        }
        return Math.round(score * 1000.0) / 1000.0;
    }

    public static Map<String, Double> readIdfFile(String idfFile) {
        Map<String, Double> idfInfo = new HashMap<>();
        InputStream inputStream = Utils.class.getResourceAsStream(idfFile);
        if(inputStream == null){
            return idfInfo;
        }
        BufferedReader idfF = new BufferedReader(new InputStreamReader(inputStream));
        String text;
        try {
            while (true) {
                if ((text = idfF.readLine()) == null){
                    break;
                }
                String[] info = text.trim().split("	");
                if (info.length != 2) {
                    continue;
                }
                idfInfo.put(info[0], Double.parseDouble(info[1]));
            }
        } catch (IOException e) {
            logger.error("readIdfFile IOException occurred..", e);
            return idfInfo;
        } finally {
            try {
                if(idfF!=null){
                    idfF.close();
                }
            } catch (IOException e) {
                logger.error("idfF input stream close fail..", e);
                throw new RuntimeException(e);
            }
        }
        return idfInfo;
    }

    public static String removePuncs(String s) {
        if(StringUtils.isEmpty(s)){
            return s;
        }
        return s.replaceAll(PUNCS, "");
    }

    public static String textNorm(String text) {
        if(StringUtils.isEmpty(text)){
            return text;
        }
        text = text.replaceAll(ZH_EN_NUM_SPACE_REX, "");
//        ToolUtils.chineseExt()
        Matcher matcher = NUM_PATTERN.matcher(text);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, an2cn(matcher.group()));
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    private static String an2cn(String an) {
        // TODO: implement this method to convert Arabic numbers to Chinese numbers
        return an;
    }

    /**
     * n=2对应python：list(nltk.bigrams(tokens))
     * @param tokens
     * @param n
     * @return
     */
    public static List<List<String>> bigrams(List<String> tokens, int n) {
        List<List<String>> ngrams = new ArrayList<>();
        for (int i = 0; i < tokens.size() - n + 1; i++) {
            List<String> childNgrams = new ArrayList<>();
            for (int j = i; j < i + n; j++) {
                childNgrams.add(tokens.get(j));
            }
            ngrams.add(childNgrams);
        }
        return ngrams;
    }

    /**
     * 对应python：[t for t in nltk.ngrams(raw_query, 2)]
     * @param text
     * @param n
     * @return
     */
    public static List<List<String>> ngrams(String text, int n) {
        List<List<String>> ngrams = new ArrayList<>();
        for (int i = 0; i < text.length() - n + 1; i++) {
            List<String> childNgrams = new ArrayList<>();
            for (int j = i; j < i + n; j++) {
                childNgrams.add(String.valueOf(text.charAt(j)));
            }
            ngrams.add(childNgrams);
        }
        return ngrams;
    }

    /**
     * 对应python：["".join(t) for t in nltk.ngrams(raw_query, 2)]
     * @param text
     * @param n
     * @return
     */
    public static List<String> ngramsString(String text, int n) {
        List<String> ngrams = new ArrayList<>();
        for (int i = 0; i < text.length() - n + 1; i++) {
            StringBuilder sb = new StringBuilder();
            for (int j = i; j < i + n; j++) {
                sb.append(text.charAt(j));
            }
            ngrams.add(sb.toString());
        }
        return ngrams;
    }


    public static void main(String[] args) {
        // ['gong1', 'zhu3', 'fen2', 'di4', 'tie3', 'zhan4']
        List<String> tokens = new ArrayList<>();
        tokens.add("gong1");
        tokens.add("zhu3");
        tokens.add("fen2");
        tokens.add("di4");
        tokens.add("tie3");
        tokens.add("zhan4");
        List<List<String>> bigrams = bigrams(tokens, 2);
        System.out.println(bigrams);

        // 公主坟
        List<List<String>> ngrams = ngrams("公主坟", 2);
        System.out.println(ngrams);

        // 公主坟
        List<String> ngramsString = ngramsString("公主坟", 2);
        System.out.println(ngramsString);
    }

}

